//////////////////////////////////////////////////////////////////////
//
//  CryEngine Source code
//	
//	File: AnimationManager.cpp
//  Implementation of Animation Manager.cpp
//
//	History:
//	January 12, 2005: Created by Ivo Herzeg <ivo@crytek.de>
//
//////////////////////////////////////////////////////////////////////
#include "StdAfx.h"

#if !defined(__CRYCG__)
#include <CryHeaders.h>	

#include "LoaderTCB.h"
#include "ModelAnimationSet.h"
#endif // !__CRYCG__
#include "LoaderCGA.h"
#include "LoaderDBA.h"
#include <StlUtils.h>

#include "ControllerPQ.h"
#include "ControllerOpt.h"


#include "AnimationManager.h"

#if !defined(__CRYCG__)

#include <StringUtils.h>
#include <CryPath.h>

#endif

#include "LMG.h"

stl::PoolAllocatorNoMT<sizeof(SAnimationSelectionProperties)> *g_Alloc_AnimSelectProps = 0;





int CAnimationManager::GetGlobalAnimID_CAF(const char* pFilePath)
{
	return this->GetGlobalIDbyFilePath_CAF(pFilePath);
}
int CAnimationManager::GetGlobalAnimID_LMG(const char* pFilePath)
{
	return this->GetGlobalIDbyFilePath_LMG(pFilePath);
}


/////////////////////////////////////////////////////////////////////
// finds the animation-asset by path-name. 
// Returns -1 if no animation was found.
// Returns the animation ID if it was found.
/////////////////////////////////////////////////////////////////////
int CAnimationManager::GetGlobalIDbyFilePath_CAF( const char * sAnimFileName)
{
	int id = m_AnimationMapCAF.GetValue(sAnimFileName);
	if (id<0)
		return -1;
	int numCAF = m_arrGlobalCAF.size();
	assert(id<numCAF);
	if (id>=numCAF)
		return -1;
	assert(!stricmp(m_arrGlobalCAF[id].GetFilePath(),sAnimFileName));
	return id;
}


/////////////////////////////////////////////////////////////////////
// finds the animation-asset by path-name. 
// Returns -1 if no animation was found.
// Returns the animation ID if it was found.
/////////////////////////////////////////////////////////////////////
int CAnimationManager::GetGlobalIDbyFilePath_AIM(const char * sAnimFileName)
{
	uint32 nCRC32 = NameCRCHelper::GetCRC(sAnimFileName);
	uint32 numAIM = m_arrGlobalAIM.size();
	for (uint32 id=0; id<numAIM; id++)
	{
		if (m_arrGlobalAIM[id].m_FilePathCRC32==nCRC32)
		{
			assert(!stricmp(m_arrGlobalAIM[id].GetFilePath(),sAnimFileName));
			return id;
		}
	}
	return -1;
}


/////////////////////////////////////////////////////////////////////
// finds the animation-asset by path-name. 
// Returns -1 if no animation was found.
// Returns the animation ID if it was found.
/////////////////////////////////////////////////////////////////////
int CAnimationManager::GetGlobalIDbyFilePath_LMG(const char * sAnimFileName)
{
	uint32 nCRC32 = NameCRCHelper::GetCRC(sAnimFileName);
	uint32 numLMG = m_arrGlobalLMG.size();
	for (uint32 id=0; id<numLMG; id++)
	{
		if (m_arrGlobalLMG[id].m_FilePathCRC32==nCRC32)
		{
			assert(!stricmp(m_arrGlobalLMG[id].GetFilePath(),sAnimFileName));
			return id;
		}
	}
	return -1;
}



////////////////////////////////////////////////////////////////////////////////////
// loads the animation with the specified name; if the animation is already loaded,
// then just returns its id
// The caller MUST TAKE CARE to bind the animation if it's already loaded before it has registered itself within this manager
// RETURNS:
//   The global animation id.
//   -1 if the animation couldn't be loaded 
// SIDE EFFECT NOTES:
//   This function does not put up a warning in the case the file was not found.
//   The caller must do so. 
int CAnimationManager::CreateGAH_CAF( const string& strFilePath )
{
	int nGlobalAnimId = GetGlobalIDbyFilePath_CAF(strFilePath);
	if (nGlobalAnimId<0)
	{
		nGlobalAnimId = int32(m_arrGlobalCAF.size());
		// add a new animation structure that will hold the info about the new animation.
		// the new animation id is the index of this structure in the array

		m_arrGlobalCAF.push_back();
		m_arrGlobalCAF[nGlobalAnimId]=GlobalAnimationHeaderCAF();

		m_arrGlobalCAF[nGlobalAnimId].SetFilePathCAF(strFilePath);
		m_animSearchHelper.AddAnimation(strFilePath, nGlobalAnimId);
		m_AnimationMapCAF.InsertValue(m_arrGlobalCAF[nGlobalAnimId].GetFilePathCRC32(), nGlobalAnimId);
	}
	return nGlobalAnimId;
}


int CAnimationManager::CreateGAH_AIM(const string& strFilePath)
{
	int nGlobalAnimId = GetGlobalIDbyFilePath_AIM(strFilePath);
	if (nGlobalAnimId<0)
	{
		nGlobalAnimId = int32(m_arrGlobalAIM.size());
		// add a new animation structure that will hold the info about the new animation.
		// the new animation id is the index of this structure in the array
		m_arrGlobalAIM.push_back(GlobalAnimationHeaderAIM());
		m_arrGlobalAIM[nGlobalAnimId].SetFilePath(strFilePath);
		//	m_AnimationMapAIM.InsertValue(m_arrGlobalAIM[nGlobalAnimId].GetFilePathCRC32(), nGlobalAnimId);
	}
	return nGlobalAnimId;
}


int CAnimationManager::CreateGAH_LMG(const string& strFilePath)
{
	int nGlobalAnimId = GetGlobalIDbyFilePath_LMG(strFilePath);
	if (nGlobalAnimId<0)
	{
		nGlobalAnimId = int32(m_arrGlobalLMG.size());
		// add a new animation structure that will hold the info about the new animation.
		// the new animation id is the index of this structure in the array
		m_arrGlobalLMG.push_back(GlobalAnimationHeaderLMG());
		m_arrGlobalLMG[nGlobalAnimId].SetFilePath(strFilePath);
		//	m_AnimationMapLMG.InsertValue(m_arrGlobalLMG[nGlobalAnimId].GetFilePathCRC32(), nGlobalAnimId);
	}
	return nGlobalAnimId;
}







bool CAnimationManager::LoadAnimationTCB(int nAnimId, DynArray<CControllerTCB>& arrNodeAnims, CryCGALoader* pCGA, uint32 unique_model_id )
{
	//	return false;
	LOADING_TIME_PROFILE_SECTION(g_pISystem);

	GlobalAnimationHeaderCAF& rGlobalAnim = m_arrGlobalCAF[nAnimId];
	int32 nStartKey				= pCGA->m_start;
	int32 nEndKey					= pCGA->m_end;

	int32 fTicksPerFrame  = TICKS_PER_FRAME;
	f32		fSecsPerTick		= SECONDS_PER_TICK;
	f32		fSecsPerFrame		= fSecsPerTick * fTicksPerFrame;
	rGlobalAnim.m_fStartSec = nStartKey * fSecsPerFrame;
	rGlobalAnim.m_fEndSec   = nEndKey   * fSecsPerFrame;
	if(rGlobalAnim.m_fEndSec<=rGlobalAnim.m_fStartSec)
		rGlobalAnim.m_fEndSec  = rGlobalAnim.m_fStartSec;//+(1.0f/30.0f);

	rGlobalAnim.m_fTotalDuration = rGlobalAnim.m_fEndSec - rGlobalAnim.m_fStartSec;
	//	assert(rGlobalAnim.m_fTotalDuration > 0);

	uint32 numCtrl = rGlobalAnim.m_nControllers;
	assert(numCtrl==0);
	if (numCtrl)
		return true;

	uint32 numController = 0;
	uint32 numAnimSize = arrNodeAnims.size();
	for(uint32 i=0; i<numAnimSize; i++)
	{
		uint32 numPos = arrNodeAnims[i].m_posTrack.num_keys();
		uint32 numRot = arrNodeAnims[i].m_rotTrack.num_keys();
		uint32 numScl = arrNodeAnims[i].m_sclTrack.num_keys();
		if (numPos+numRot+numScl) 
			numController++;
	}
	//	rGlobalAnim.m_arrController.resize(numController);
	rGlobalAnim.m_arrController.resize(numController);// = new IController_AutoPtr[numController];
	rGlobalAnim.m_nControllers = numController;
	rGlobalAnim.m_nControllers2 = numController;

	for(uint32 i=0, j = 0; i<numAnimSize; i++)
	{
		uint32 numPos = arrNodeAnims[i].m_posTrack.num_keys();
		uint32 numRot = arrNodeAnims[i].m_rotTrack.num_keys();
		uint32 numScl = arrNodeAnims[i].m_sclTrack.num_keys();
		if (numPos+numRot+numScl) 
		{
			CControllerTCB* pControllerTCB = new CControllerTCB();
			*pControllerTCB =	arrNodeAnims[i];

			pControllerTCB->m_nControllerId=unique_model_id+i;	//g_nControllerJID+i;

			IController* pController = (IController*)pControllerTCB;
			PREFAST_SUPPRESS_WARNING(6386)
			rGlobalAnim.m_arrController[j] = static_cast<IController*>(pController);
			++j;
		}
	}

	std::sort( rGlobalAnim.m_arrController.begin(), rGlobalAnim.m_arrController.end(), AnimCtrlSortPred() );

	rGlobalAnim.InitControllerLookup(numController);	
	rGlobalAnim.OnAssetCreated();
	rGlobalAnim.m_nFlags |= CA_ASSET_TCB;
	return true;
}














// notifies the controller manager that this client doesn't use the given animation any more.
// these calls must be balanced with AnimationAddRef() calls
void CAnimationManager::AnimationReleaseCAF( GlobalAnimationHeaderCAF& rCAF)
{
	int32 RefCount = rCAF.m_nRef_by_Model;
	if (RefCount==0)
		rCAF.Release();
}
void CAnimationManager::AnimationReleaseAIM( GlobalAnimationHeaderAIM& rAIM)
{
	int32 RefCount = rAIM.m_nRef_by_Model;
	if (RefCount==0)
		rAIM.Release();
}
void CAnimationManager::AnimationReleaseLMG( GlobalAnimationHeaderLMG& rLMG)
{
	int32 RefCount = rLMG.m_nRef_by_Model;
	if (RefCount==0)
		rLMG.Release();
}







int32 CAnimationManager::GetAnimEventsCount_CAF(int32 nGlobalID) const
{
	int32 numAnims = (int32)m_arrGlobalCAF.size();
	if (nGlobalID<0)
		return -1;
	if (nGlobalID >= numAnims)
		return -1;
	uint32 numAnimEvents = m_arrGlobalCAF[nGlobalID].m_AnimEventsCAF.size();
	return (int)numAnimEvents;
} 
int32 CAnimationManager::GetAnimEventsCount_CAF(const char* pFilePath )
{
	int32 nGlobalID = GetGlobalIDbyFilePath_CAF(pFilePath );
	return GetAnimEventsCount_CAF(nGlobalID);
}


void CAnimationManager::AddAnimEvent_CAF(int nGlobalID, const char* pName, const char* pParameter, const char* pBone, float fTime, const Vec3& vOffset, const Vec3& vDir, const char* pModel)
{
	if (nGlobalID >= 0 && nGlobalID < int(m_arrGlobalCAF.size()))
	{
		AnimEvents aevents;
		aevents.m_time=fTime;
		aevents.m_strCustomParameter=pParameter;
		aevents.m_strEventName=pName;
		aevents.m_EventNameLowercaseCRC32=g_pCrc32Gen->GetCRC32Lowercase(pName);
		aevents.m_strBoneName=pBone;
		aevents.m_vOffset=vOffset;
		aevents.m_vDir=vDir;
		aevents.m_strModelName=pModel;
		m_arrGlobalCAF[nGlobalID].m_AnimEventsCAF.push_back(aevents);
	}
}
void CAnimationManager::AddAnimEvent_LMG(int nGlobalID, const char* pName, const char* pParameter, const char* pBone, float fTime, const Vec3& vOffset, const Vec3& vDir, const char* pModel)
{
	if (nGlobalID >= 0 && nGlobalID < int(m_arrGlobalCAF.size()))
	{
		AnimEvents aevents;
		aevents.m_time=fTime;
		aevents.m_strCustomParameter=pParameter;
		aevents.m_strEventName=pName;
		aevents.m_EventNameLowercaseCRC32=g_pCrc32Gen->GetCRC32Lowercase(pName);
		aevents.m_strBoneName=pBone;
		aevents.m_vOffset=vOffset;
		aevents.m_vDir=vDir;
		aevents.m_strModelName=pModel;
		m_arrGlobalLMG[nGlobalID].m_AnimEventsLMG.push_back(aevents);
	}
}


void CAnimationManager::DeleteAllEventsForCAF(int nGlobalID)
{
	if (nGlobalID<0)
		return;
	int numCAF = m_arrGlobalCAF.size();
	if (nGlobalID < numCAF)
		m_arrGlobalCAF[nGlobalID].m_AnimEventsCAF.clear();
}

void CAnimationManager::DeleteAllEventsForLMG(int nGlobalID)
{
	if (nGlobalID<0)
		return;
	int numLMG = m_arrGlobalLMG.size();
	if (nGlobalID < numLMG)
		m_arrGlobalLMG[nGlobalID].m_AnimEventsLMG.clear();
}





//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
bool CAnimationManager::CreateGlobalHeaderDBA(DynArray<string>& arrFilePathDBA)
{
	uint32 numDBAs = arrFilePathDBA.size();
	for (size_t d=0; d<numDBAs; d++)
	{
		const char* pFilePathDBA = arrFilePathDBA[d];

		uint32 numHeaders = m_arrGlobalHeaderDBA.size();
		uint32 exits=0;
		for (uint32 dba=0; dba<numHeaders; dba++)
		{
			const char* pname = m_arrGlobalHeaderDBA[dba].m_strFilePathDBA;
			int32 same = stricmp(pname,pFilePathDBA);
			if (same==0)
			{
				exits=1;
				break;
			}
		}

		if (exits==0)
		{
			//create GloablHeaderDBA and load data
			CGlobalHeaderDBA HeaderDBA;
			HeaderDBA.CreateDatabaseDBA( pFilePathDBA );
			m_arrGlobalHeaderDBA.push_back(HeaderDBA);
		}
	}
	return 0;
}



//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
bool CAnimationManager::DBA_PreLoad(const char* pFilePathDBA, bool highPriority)
{
	stack_string strPath = pFilePathDBA;
	CryStringUtils::UnifyFilePath(strPath);

	uint32 numHeaders = m_arrGlobalHeaderDBA.size();
	for (uint32 dba=0; dba<numHeaders; dba++)
	{
		const char* pname = m_arrGlobalHeaderDBA[dba].m_strFilePathDBA;
		int32 same = stricmp(pname,strPath.c_str());
		if (same==0)
		{
			if (Console::GetInst().ca_UseIMG_CAF)
				m_arrGlobalHeaderDBA[dba].StartStreamingDBA(highPriority);
			else
				m_arrGlobalHeaderDBA[dba].LoadDatabaseDBA("");
			return 1;
		}
	}

	//create GloablHeaderDBA and load data
	CGlobalHeaderDBA HeaderDBA;
	HeaderDBA.CreateDatabaseDBA( strPath.c_str() );
	m_arrGlobalHeaderDBA.push_back(HeaderDBA);
	uint32 lastDBA = m_arrGlobalHeaderDBA.size()-1;

	if (Console::GetInst().ca_UseIMG_CAF)
		m_arrGlobalHeaderDBA[lastDBA].StartStreamingDBA(highPriority);
	else
		m_arrGlobalHeaderDBA[lastDBA].LoadDatabaseDBA("");

	return 0;
}

bool CAnimationManager::DBA_LockStatus(const char* pFilePathDBA, uint32 status, bool highPriority )
{
	//return 1;
	stack_string strPath = pFilePathDBA;
	CryStringUtils::UnifyFilePath(strPath);

	uint32 numHeaders = m_arrGlobalHeaderDBA.size();
	for (uint32 dba=0; dba<numHeaders; dba++)
	{
		const char* pname = m_arrGlobalHeaderDBA[dba].m_strFilePathDBA;
		int32 same = stricmp(pname,strPath.c_str());
		if (same==0)
		{
			m_arrGlobalHeaderDBA[dba].m_bDBALock=status;
			if (status)
			{
				if (Console::GetInst().ca_UseIMG_CAF)
					m_arrGlobalHeaderDBA[dba].StartStreamingDBA(highPriority);
				else
					m_arrGlobalHeaderDBA[dba].LoadDatabaseDBA("");
			}
			return 1;
		}
	}
	return 0;
}

bool CAnimationManager::DBA_Unload(const char* pFilePathDBA)
{
	stack_string strPath = pFilePathDBA;
	CryStringUtils::UnifyFilePath(strPath);

	uint32 numHeadersDBA = m_arrGlobalHeaderDBA.size();
	if (numHeadersDBA==0)
		return 1;

	CGlobalHeaderDBA* parrGlobalDBA = &m_arrGlobalHeaderDBA[0];
	for (uint32 d=0; d<numHeadersDBA; d++)
		parrGlobalDBA[d].m_nUsedAnimations=0;

	uint32 numHeadersCAF = m_arrGlobalCAF.size();
	if (numHeadersCAF)
	{	
		GlobalAnimationHeaderCAF* parrGlobalCAF = &m_arrGlobalCAF[0];
		for (uint32 i=0; i<numHeadersCAF; i++)
		{
			if (parrGlobalCAF[i].m_nRef_at_Runtime==0)
				continue;
			uint32 nCRC32 = parrGlobalCAF[i].m_FilePathDBACRC32;
			if (nCRC32)
			{
				for (uint32 d=0; d<numHeadersDBA; d++)
				{
					if (nCRC32==parrGlobalDBA[d].m_FilePathDBACRC32)
					{
						parrGlobalDBA[d].m_nUsedAnimations++;
						parrGlobalDBA[d].m_nTCount = 0;
					}
				}
			}
		}
	}

	for (uint32 dba=0; dba<numHeadersDBA; dba++)
	{
		const char* pname = m_arrGlobalHeaderDBA[dba].m_strFilePathDBA;
		int32 same = stricmp(pname,strPath.c_str());
		if (same==0)
		{
			m_arrGlobalHeaderDBA[dba].m_bDBALock=0;

			if (m_arrGlobalHeaderDBA[dba].m_pDatabaseInfo==0)
				return 1;
			if (m_arrGlobalHeaderDBA[dba].m_nUsedAnimations)
			{
				//CryFatalError("CryAnimation: Animations still in use. Can't delete DBA: %s",m_arrGlobalHeaderDBA[dba].m_strFilePathDBA.c_str() );
				continue;
			}
			m_arrGlobalHeaderDBA[dba].DeleteDatabaseDBA();
			return 1;
		}
	}

	return 0;
}

void CAnimationManager::InitialiseRunTimePools()
{
	LMG::InitialiseRuntimePool();
}

void CAnimationManager::DestroyRunTimePools()
{
	LMG::DestroyRuntimePool();
}

bool CAnimationManager::Unload_All_Animation()
{
	uint32 numHeadersCAF = m_arrGlobalCAF.size();
	if (numHeadersCAF)
	{	
		//remove all CAFs
		GlobalAnimationHeaderCAF* parrGlobalCAF = &m_arrGlobalCAF[0];
		for (uint32 i=0; i<numHeadersCAF; i++)
		{
			parrGlobalCAF[i].m_nRef_at_Runtime=0;
				uint32 nCRC32 = parrGlobalCAF[i].m_FilePathDBACRC32;
			if (nCRC32==0 || nCRC32==-1)
				parrGlobalCAF[i].ClearControllers();
		}
	}

	uint32 numHeadersDBA = m_arrGlobalHeaderDBA.size();
	for (uint32 dba=0; dba<numHeadersDBA; dba++)
	{
		m_arrGlobalHeaderDBA[dba].m_nUsedAnimations=0;
		if (m_arrGlobalHeaderDBA[dba].m_pDatabaseInfo==0)
			continue;
		m_arrGlobalHeaderDBA[dba].DeleteDatabaseDBA();
	}

	InitialiseRunTimePools();

	return 1;
}

bool CAnimationManager::DBA_Unload_All()
{
	uint32 numHeadersDBA = m_arrGlobalHeaderDBA.size();
	if (numHeadersDBA==0)
		return 1;

	CGlobalHeaderDBA* parrGlobalDBA = &m_arrGlobalHeaderDBA[0];
	for (uint32 d=0; d<numHeadersDBA; d++)
		parrGlobalDBA[d].m_nUsedAnimations=0;

	for (uint32 d=0; d<numHeadersDBA; d++)  //unlock all
			parrGlobalDBA[d].m_bDBALock=0;

	uint32 numHeadersCAF = m_arrGlobalCAF.size();
	if (numHeadersCAF)
	{	
		GlobalAnimationHeaderCAF* parrGlobalCAF = &m_arrGlobalCAF[0];
		for (uint32 i=0; i<numHeadersCAF; i++)
		{
		//	parrGlobalCAF[i].m_nRef_at_Runtime=0;

			uint32 nCRC32 = parrGlobalCAF[i].m_FilePathDBACRC32;
			if (nCRC32==0 || nCRC32==-1)
				parrGlobalCAF[i].ClearControllers();

			if (parrGlobalCAF[i].m_nRef_at_Runtime==0)
				continue;
			if (nCRC32)
			{
				for (uint32 d=0; d<numHeadersDBA; d++)
				{
					if (nCRC32==parrGlobalDBA[d].m_FilePathDBACRC32)
					{
						parrGlobalDBA[d].m_nUsedAnimations++;
						parrGlobalDBA[d].m_nTCount = 0;
					}
				}
			}
		}
	}

	for (uint32 dba=0; dba<numHeadersDBA; dba++)
	{
		if (m_arrGlobalHeaderDBA[dba].m_pDatabaseInfo==0)
			continue;
		if (m_arrGlobalHeaderDBA[dba].m_nUsedAnimations)
		{
			//CryFatalError("CryAnimation: Animations still in use. Can't delete DBA : %s  used: %d",m_arrGlobalHeaderDBA[dba].m_strFilePathDBA.c_str(), m_arrGlobalHeaderDBA[dba].m_nUsedAnimations );
			continue;
		}
		m_arrGlobalHeaderDBA[dba].DeleteDatabaseDBA();
	}

	return 1;
}



int CAnimationManager::ReloadCAF(const char* szFilePathCAF)
{
	uint32 nCRC32 = g_pCrc32Gen->GetCRC32Lowercase(szFilePathCAF);

	uint32 numAIM = m_arrGlobalAIM.size();
	for (uint32 id=0; id<numAIM; id++)
	{
		GlobalAnimationHeaderAIM& rAIM = m_arrGlobalAIM[id];
		if (rAIM.m_FilePathCRC32==nCRC32)
		{
			assert( !strcmp(rAIM.GetFilePath(),szFilePathCAF) );
			rAIM.m_nControllers=0;
			int status =	rAIM.LoadAIM();
			if(status) 
			{
				CryLog( "Hot loaded animation CAF-file for an Directional-Pose: %s", rAIM.m_FilePath.c_str() );

				CCharacterModel* pModel = g_pCharacterManager->GetModelByAimPoseID(id);
			
				CModelSkeleton* pModelSkeleton = &pModel->m_ModelSkeleton;

				uint32 numDB = pModel->m_ModelSkeleton.m_AimDirBlends.size();
				for (uint32 d=0; d<numDB; d++)
				{
					const char* strAnimToken = pModel->m_ModelSkeleton.m_AimDirBlends[d].m_AnimToken;
					uint32 IsAIM = (CryStringUtils::stristr(rAIM.m_FilePath.c_str(),strAnimToken) != 0);
					if (IsAIM)
					{
						rAIM.ProcessAimPoses(pModel,pModelSkeleton->m_AimDirBlends,pModelSkeleton->m_AimIK_Rot,pModelSkeleton->m_AimIK_Pos);
						CryLog( "Re-Processed Aim-Pose: %s", rAIM.m_FilePath.c_str() );
						break;
					}
				}

				uint32 numLB = pModel->m_ModelSkeleton.m_LookDirBlends.size();
				for (uint32 d=0; d<numLB; d++)
				{
					const char* strAnimToken = pModel->m_ModelSkeleton.m_LookDirBlends[d].m_AnimToken;
					uint32 IsLOOK = (CryStringUtils::stristr(rAIM.m_FilePath.c_str(),strAnimToken) != 0);
					if (IsLOOK)
					{
						rAIM.ProcessAimPoses(pModel,pModelSkeleton->m_LookDirBlends,pModelSkeleton->m_LookIK_Rot,pModelSkeleton->m_LookIK_Pos);
						CryLog( "Re-Processed Look-Pose: %s", rAIM.m_FilePath.c_str() );
						break;
					}
				}

			} 

			return status;
		}
	}

	//asset not found in the AIM-array
	//now check the CAF array
	uint32 numCAF = m_arrGlobalCAF.size();
	for (uint32 id=0; id<numCAF; id++)
	{
		GlobalAnimationHeaderCAF& rCAF = m_arrGlobalCAF[id];
		if (rCAF.m_FilePathCRC32==nCRC32)
		{
			assert(!strcmp(rCAF.GetFilePath(),szFilePathCAF));
			rCAF.m_nControllers=0;
			int status = rCAF.LoadCAF(0);
			if(status) 
				CryLog( "Hot loaded animation CAF-file: %s",rCAF.m_FilePath.c_str() );
			return status;
		}
	}

	return -1;
}

uint32 CAnimationManager::GetDBACRC32fromFilePath(const char* szFilePathCAF)
{
	uint32 nCRC32 = g_pCrc32Gen->GetCRC32Lowercase(szFilePathCAF);

	uint32 numCAF = m_arrGlobalCAF.size();
	for (uint32 id=0; id<numCAF; id++)
	{
		GlobalAnimationHeaderCAF& rCAF = m_arrGlobalCAF[id];
		if (rCAF.m_FilePathCRC32==nCRC32)
			return rCAF.m_FilePathDBACRC32;
	}
	return 0;
}



bool CAnimationManager::IsDatabaseInMemory(uint32 nDBACRC32)
{
	size_t numDBA_Files = m_arrGlobalHeaderDBA.size();
	for (uint32 d=0; d<numDBA_Files; d++)
	{
		if (nDBACRC32==m_arrGlobalHeaderDBA[d].m_FilePathDBACRC32)
			return m_arrGlobalHeaderDBA[d].InMemory();
	}
	return 0;
}




//------------------------------------------------------------------------------
// Unloads animation from memory and remove 
//------------------------------------------------------------------------------
void CAnimationManager::UnloadAnimationCAF(GlobalAnimationHeaderCAF& rCAF)
{
	if (Console::GetInst().ca_UnloadAnimationCAF==0)
		return;
	uint32 requested = rCAF.IsAssetRequested();
	if	(requested)
		return;
	assert(rCAF.GetControllersCount());

	if (g_pCharacterManager->m_pStreamingListener)
	{
		int32 globalID = GetGlobalIDbyFilePath_CAF(rCAF.GetFilePath());
		g_pCharacterManager->m_pStreamingListener->NotifyAnimUnloaded(globalID);
	}
	
	rCAF.ClearControllers();
//	g_pISystem->Warning( VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING, VALIDATOR_FLAG_FILE,0,	"UnloadingAsset: Name: %s",rCAF.GetFilePath() );
}

void CAnimationManager::UnloadAnimationAIM(int nGLobalAnimID)
{
	assert(m_arrGlobalAIM[nGLobalAnimID].GetControllersCount());
	m_arrGlobalAIM[nGLobalAnimID].ClearControllers();
}



//struct AnimSearchHelper {
//
//	typedef std::vector<int> TIndexVector;
//	typedef std::map<uint32 /*crc*/, TIndexVector*/*vector of indexes*/> TFoldersVector;
//
//	void AddAnimation(uint32 crc, uint32 gahIndex);
//	void AddAnimation(const string& path, uint32 gahIndex);
//
//	TIndexVector * GetAnimationsVector(uint32 crc);
//	TIndexVector * GetAnimationsVector(const string& path);
//
//};

void AnimSearchHelper::AddAnimation(uint32 crc, uint32 gahIndex)
{
	TIndexVector * vect = GetAnimationsVector(crc);
	if (!vect)
	{
		vect = new TIndexVector;
		m_AnimationsMap[crc] = vect;
	}

	vect->push_back(gahIndex);
}

void AnimSearchHelper::AddAnimation(const string& path, uint32 gahIndex)
{
	stack_string pathDir = PathUtil::GetPath(path);
	PathUtil::ToUnixPath(pathDir);
	if (strcmp(pathDir, "animations/human/male/behavior/fear/") == 0)
		int A = 0;
	if (strcmp(pathDir, "animations\\human\\male\\behavior\\fear\\") == 0)
		int A = 0;

	uint32 crc = gEnv->pSystem->GetCrc32Gen()->GetCRC32Lowercase(pathDir); 
	AddAnimation(crc, gahIndex);
}

AnimSearchHelper::TIndexVector * AnimSearchHelper::GetAnimationsVector(uint32 crc)
{
	TFoldersVector::iterator it = m_AnimationsMap.find(crc);

	if (it!= m_AnimationsMap.end())
		return it->second;

	return 0;
}

AnimSearchHelper::TIndexVector * AnimSearchHelper::GetAnimationsVector(const string& path)
{
	uint32 crc = gEnv->pSystem->GetCrc32Gen()->GetCRC32Lowercase(path);
	return GetAnimationsVector(crc);
}

void AnimSearchHelper::Clear()
{
	for ( TFoldersVector::iterator it = m_AnimationsMap.begin(), end = m_AnimationsMap.end(); it != end; ++it) {
		delete it->second;
	}
}




size_t CAnimationManager::GetSizeOfDBA()
{
	size_t nSize=0;
	uint32 numDBAs = m_arrGlobalHeaderDBA.size();
	for (uint32 i=0; i<numDBAs; i++)
		nSize += m_arrGlobalHeaderDBA[i].SizeOf_DBA();
	return nSize;
}

void CAnimationManager::GetMemoryUsage(class ICrySizer* pSizer) const
{
	SIZER_SUBCOMPONENT_NAME(pSizer, "AnimationKeys");
	pSizer->AddObject( m_AnimationMapCAF );
	pSizer->AddObject( m_arrGlobalCAF );
	pSizer->AddObject( m_arrGlobalAIM );
	pSizer->AddObject( m_arrGlobalLMG );
	pSizer->AddObject( m_arrGlobalHeaderDBA );
}

void CAnimationManager::DebugAnimUsage(uint32 printtxt)
{
	DEFINE_PROFILER_FUNCTION();

#ifndef CONSOLE_CONST_CVAR_MODE
//	if (Console::GetInst().ca_DebugAnimMemTracking==0)
//		return;
	if (m_shuttingDown)
		return;
	float fRed[4]		= {1,0,0,1};
	float fGreen[4] = {0,1,0,1};
	float fBlue[4]	= {0,0,1,1};

	size_t numDBA_Files = m_arrGlobalHeaderDBA.size();

	for (uint32 d=0; d<numDBA_Files; d++)
		m_arrGlobalHeaderDBA[d].m_nEmpty=1;

	for (uint32 x=0; x<numDBA_Files; x++)
	{
		uint32 biggest=0;
		int32 b=-1;	
		for (uint32 d=0; d<numDBA_Files; d++)
		{
			uint32 nUsed			= m_arrGlobalHeaderDBA[d].m_nEmpty;
			size_t nSizeOfDBA = m_arrGlobalHeaderDBA[d].SizeOf_DBA();
			if (biggest<nSizeOfDBA && nUsed)
			{
				biggest=nSizeOfDBA;
				b=d;
			}
		}

		if (b>-1)
		{
			m_arrGlobalHeaderDBA[b].m_nEmpty=0;
			const char* pName  = m_arrGlobalHeaderDBA[b].m_strFilePathDBA;
			uint32 nDBACRC32   = m_arrGlobalHeaderDBA[b].m_FilePathDBACRC32;
			uint32 nUsedAssets = m_arrGlobalHeaderDBA[b].m_nUsedAnimations;
			size_t nSizeOfDBA  = m_arrGlobalHeaderDBA[b].SizeOf_DBA();
			size_t nTCount     = g_AnimationManager.m_arrGlobalHeaderDBA[b].m_nTCount;
			uint32 nLock			 = m_arrGlobalHeaderDBA[b].m_bDBALock;

			if (m_arrGlobalHeaderDBA[b].m_pDatabaseInfo==0)
			{
					//	fColorGreen[3]=0.4f;
					//	g_pIRenderer->Draw2dLabel( 1,g_YLine, 0.9f, fColorGreen, false,"UsedAssets: %04d  nTCount: %08x  Size: %08d  FilePathDBA: %s",nUsedAssets,nTCount,nSizeOfDBA,pName );	
					//	g_YLine+=9.0f; 
			} 
			else
			{
				float fColor[4]		= {0,1,0,1};
				if (nLock)
				{
					fColor[0]=1.0f;
					fColor[1]=0.0f;
				}
				if (printtxt)
				{
						g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.1f, fColor, false,"UsedAssets: %04d  nTCount: %08x  Size: %08d  FilePathDBA: %s",nUsedAssets,nTCount,nSizeOfDBA,pName );	
						g_YLine+=11.0f; 
				}
			}
		}
	}
	g_YLine+=16.0f; 






	size_t nAnimationManager=sizeof(CAnimationManager);

	size_t nMap = m_AnimationMapCAF.GetAllocMemSize();



	size_t nFilePathSize = 0;

	{
		uint32 numCAF    = m_arrGlobalCAF.size();
		for (uint32 i=0; i<numCAF; i++)
			nFilePathSize += m_arrGlobalCAF[i].m_FilePath.capacity();
		uint32 numAIM    = m_arrGlobalAIM.size();
		for (uint32 i=0; i<numAIM; i++)
			nFilePathSize += m_arrGlobalAIM[i].m_FilePath.capacity();
		uint32 numLMG    = m_arrGlobalLMG.size();
		for (uint32 i=0; i<numLMG; i++)
			nFilePathSize += m_arrGlobalLMG[i].m_FilePath.capacity();

		numCAF = m_arrGlobalCAF.capacity();
		numAIM = m_arrGlobalAIM.capacity();
		numLMG = m_arrGlobalLMG.capacity();

		size_t nEmptyHeaderSize = 0;
		nEmptyHeaderSize += numCAF*sizeof(GlobalAnimationHeaderCAF);
		nEmptyHeaderSize += numAIM*sizeof(GlobalAnimationHeaderAIM);
		nEmptyHeaderSize += numLMG*sizeof(GlobalAnimationHeaderLMG);
		if (printtxt)
		{
			g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.0f, fRed, false,"size of path names: %d bytes     Empty Headers: %d bytes",(int32)nFilePathSize,(int32)nEmptyHeaderSize);	
			g_YLine+=16.0f; 
		}
	}




	//calculate size of DBAs
	size_t nDBAalloc    = GetSizeOfDBA();
	size_t nClientAlloc = m_arrClients.get_alloc_size();
	nAnimationManager+=nDBAalloc;
	nAnimationManager+=nClientAlloc;
	if (printtxt)
	{
		g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.0f, fRed, false,"DBA: %d KBytes   Clients: %d KBytes  map: %d KBytes", (int32)(nDBAalloc/1024), (int32)(nClientAlloc/1024), (int32)(nMap/1024) );	
		g_YLine+=16.0f; 
	}

	uint32 nUsedCAFs = 0;
	{
		uint32 numCAF    = m_arrGlobalCAF.size();
		size_t  nSize = m_arrGlobalCAF.get_alloc_size()+nMap; 
		for (uint32 i=0; i<numCAF; i++)
			nSize += m_arrGlobalCAF[i].SizeOfCAF();
		uint32 nNumHeaders = m_arrGlobalCAF.size();
		uint32 nLoaded = 0;
		uint32 nSizeOfLoadedKeys = 0;
		for (uint32 i=0; i<nNumHeaders; i++)
		{
			uint32 IsAssetLoaded = m_arrGlobalCAF[i].IsAssetLoaded();
			uint32 IsDBAFile		 = m_arrGlobalCAF[i].m_FilePathDBACRC32;
			if (m_arrGlobalCAF[i].m_FilePathDBACRC32==-1)
				IsDBAFile=0;

			if (IsAssetLoaded && IsDBAFile==0)
			{
				nLoaded++;
				uint32 nSizeOfCAF = m_arrGlobalCAF[i].SizeOfCAF();
				nSizeOfLoadedKeys += nSizeOfCAF;

				uint32 nRef_at_Runtime = m_arrGlobalCAF[i].m_nRef_at_Runtime;

				if (printtxt && Console::GetInst().ca_DebugAnimUsage==2)
				{
					g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.1f, fGreen, false,"CafInMemory: %7d ref: %5d FilePath: %s",nSizeOfCAF,nRef_at_Runtime,m_arrGlobalCAF[i].m_FilePath.c_str() );	
					g_YLine+=11.0f; 
				}

				if (printtxt && Console::GetInst().ca_DebugAnimUsage&4)
					CryLogAlways("CafInMemory: %07d FilePath: %s",nSizeOfCAF,m_arrGlobalCAF[i].m_FilePath.c_str() );
			}
			nUsedCAFs   += (m_arrGlobalCAF[i].m_nTouchedCounter != 0);
		}

		if (printtxt && Console::GetInst().ca_DebugAnimUsage&4)
		{
			CryLogAlways("nSizeOfLoadedKeys: %07d",nSizeOfLoadedKeys );
			Console::GetInst().ca_DebugAnimUsage&=3;
		}
		if (printtxt)
		{
			g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.0f, fRed, false,"CAF: %04d    Loaded: %04d    Used: %04d   Memory: %05d / %05d KBytes",nNumHeaders, nLoaded,nUsedCAFs,(int32)(nSize/1024),nSizeOfLoadedKeys/1024 );	
			g_YLine+=16.0f; 
		}
		nAnimationManager+=nSize;
	}

	{
		uint32 numAIM = m_arrGlobalAIM.size();
		size_t  nSize = m_arrGlobalAIM.get_alloc_size(); 
		for (uint32 i=0; i<numAIM; i++)
			nSize += m_arrGlobalAIM[i].SizeOfAIM();

		uint32 nNumHeaders = m_arrGlobalAIM.size();
		uint32 nLoaded = 0;
		uint32 nUsed = 0;
		for (uint32 i=0; i<nNumHeaders; i++)
		{
			nLoaded += (m_arrGlobalAIM[i].IsAssetLoaded() != 0);
			nUsed   += (m_arrGlobalAIM[i].m_nTouchedCounter != 0);
		}
		if (printtxt)
		{
			g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.0f, fRed, false,"AIM: %04d    Loaded: %04d    Used: %04d   Memory: %05d KBytes",nNumHeaders, nLoaded,nUsed,(int32)(nSize/1024) );	
			g_YLine+=16.0f; 
		}
		nAnimationManager+=nSize;
	}


	{
		uint32 numLMG = m_arrGlobalLMG.size();
		size_t  nSize = m_arrGlobalLMG.get_alloc_size(); 
		for (uint32 i=0; i<numLMG; i++)
			nSize += m_arrGlobalLMG[i].SizeOfLMG();

		uint32 nNumHeaders = m_arrGlobalLMG.size();
		uint32 nLoaded = 0;
		uint32 nUsed = 0;
		for (uint32 i=0; i<nNumHeaders; i++)
		{
			nLoaded += (m_arrGlobalLMG[i].IsAssetLoaded() != 0);
			nUsed   += (m_arrGlobalLMG[i].m_nTouchedCounter != 0);
		}
		if (printtxt)
		{
			g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.0f, fRed, false,"LMG: %04d    Loaded: %04d    Used: %04d  Memory: %05d KBytes",nNumHeaders, nLoaded,nUsed,(int32)(nSize/1024)  );	
			g_YLine+=16.0f; 
		} 
		nAnimationManager+=nSize;
	}


	m_AnimMemoryTracker.m_nAnimsCurrent=nAnimationManager;

	m_AnimMemoryTracker.m_nGlobalCAFs=m_arrGlobalCAF.size();
	m_AnimMemoryTracker.m_nUsedGlobalCAFs=nUsedCAFs;


	if (m_AnimMemoryTracker.m_nAnimsMax<nAnimationManager)
		m_AnimMemoryTracker.m_nAnimsMax=nAnimationManager;

	uint64 average=0;
	if (m_AnimMemoryTracker.m_nAnimsCounter || m_AnimMemoryTracker.m_nAnimsCurrent>7211000)
	{
		m_AnimMemoryTracker.m_nAnimsCounter++;
		m_AnimMemoryTracker.m_nAnimsAdd+=nAnimationManager;
		average=m_AnimMemoryTracker.m_nAnimsAdd/m_AnimMemoryTracker.m_nAnimsCounter;
	}

	static uint32 mupdate=0x1f;
	mupdate++;	
	mupdate&=0x7f;
	if (mupdate==0)
		g_pCharacterManager->DebugModelCache(0);

	if (printtxt)
	{
		g_YLine+=10.0f; 
		g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.2f, fBlue, false,"nAnimsPerFrame: %4d  nAnimsMax: %4d nAnimsAvrg: %4d", m_AnimMemoryTracker.m_nAnimsCurrent/1024, m_AnimMemoryTracker.m_nAnimsMax/1024, (uint32)(average/1024) );	
		g_YLine+=25.0f; 
		g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.2f, fRed, false,"CharInstances:%3d (Mem: %4dKB) SkinInstances:%3d (Mem: %4dKB)  Models:%3d (Mem: %4dKB)",m_AnimMemoryTracker.m_numTCharInstances,m_AnimMemoryTracker.m_nTotalCharMememory/1024, m_AnimMemoryTracker.m_numTSkinInstances,m_AnimMemoryTracker.m_nTotalSkinMememory/1024,  m_AnimMemoryTracker.m_numModels,m_AnimMemoryTracker.m_nTotalMMememory/1024);	
		g_YLine+=25.0f; 
		g_pIRenderer->Draw2dLabel( 1,g_YLine, 3.0f, fRed, false,"Total: %4dKB",(m_AnimMemoryTracker.m_nTotalCharMememory + m_AnimMemoryTracker.m_nTotalSkinMememory + m_AnimMemoryTracker.m_nTotalMMememory + m_AnimMemoryTracker.m_nAnimsCurrent)/1024);	
	}
#endif
}


#include UNIQUE_VIRTUAL_WRAPPER(IAnimEvents)
